/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package tetris.model;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JOptionPane;
import tetris.model.events.FigureActionEvent;
import tetris.model.events.FigureActionListener;
import tetris.model.events.GlassEvent;
import tetris.model.events.GlassListener;
import tetris.model.navigation.Direction;
import tetris.model.navigation.Position;
import tetris.view.GameFieldPanel;

/**
 * Класс судьи.
 * @author vadim
 */
public class Judge implements FigureActionListener, GlassListener {
    private Glass _glass;
    private Glass _nextFigure;
    private Model _model;
    private ShiftTask _shiftTask;
    private final Timer _timer;
    private Timer _shiftTimer;
    private final Task _task;
    private GameFieldPanel _panel;
    private Figure _shiftFigure;
    
    /**
     * Конструктор. 
     * @param model текущая модель игры.
     * @param glass стакан.
     * @param nextFigure виджет следующей фигуры.
     */
    public Judge(Model model, Glass glass, Glass nextFigure) {
        _glass = glass;
        _nextFigure = nextFigure;
        _model = model;
        _glass.setCurrentFigure(generateNextFigure());
        _glass.currentFigure().addListener(this);
        _nextFigure.setCurrentFigure(generateNextFigure());   
        _nextFigure.currentFigure().setPosition(_nextFigure, new Position(3,5));
        _glass.addListener(this);
        _timer = new Timer();
        _task = new Task();
        _timer.schedule(_task,500,500);
    }

    /**
     * Реакция судьи на движение фигуры.
     * @param e сообщение от фигуры.
     */
    @Override
    public void figureMakedMove(FigureActionEvent e) {
        
    }

    /**
     * Реакция судьи на поворот фигуры.
     * @param e сообщение от фигуры.
     */
    @Override
    public void figureAreTurned(FigureActionEvent e) {
        
    }

    /**
     * Реакция судьи на падение фигуры.
     * @param e сообщение от фигуры.
     */
    @Override
    public void figureAreFallen(FigureActionEvent e) {
        Figure shiftFigure = (Figure)e.getSource();
        if (shiftFigure == _glass.currentFigure()) {
            if (_shiftTimer == null) {
                _shiftFigure = shiftFigure;
                _shiftTimer = new Timer();
                _shiftTask = new ShiftTask(this);
                _shiftTimer.schedule(_shiftTask,500);
            }
        }
        else
        {
            _glass.secondFigures().remove(shiftFigure);
            _glass.figureAreFallen(new FigureActionEvent(shiftFigure));
        }
    }

    private class ShiftTask extends TimerTask {
        Judge _judge;
        
        public ShiftTask(Judge judge) {
            _judge = judge;
        }
        
        @Override
        public void run() {
           if (!_shiftFigure.moveIsPossible(Direction.down())) {
               _nextFigure.currentFigure().clearListeners();
               _glass.setCurrentFigure(_nextFigure.currentFigure());
               _glass.currentFigure().addListener(_judge);
               _glass.currentFigure().addListener(_panel);
               _nextFigure.setCurrentFigure(generateNextFigure());
               _nextFigure.currentFigure().setPosition(_nextFigure, new Position(3,3));
               _glass.figureAreFallen(new FigureActionEvent(_shiftFigure));
           }
           _shiftTimer = null;
        }   
    }
    
    private class Task extends TimerTask {

        @Override
        public void run() {
            _glass.currentFigure().move(Direction.down());
            for (int i = 0; i < _glass.secondFigures().size(); i++) {
                _glass.secondFigures().get(i).move(Direction.down());
            }
        }   
    }
    
    /**
     * Статическая функция генерирует случайную фигуру.
     * @return сгенерированная фигура.
     */
    public static Figure generateNextFigure() {
        Random rand = new java.util.Random();
        Figure result;
        int number = rand.nextInt(8);
        result = getFigureByNumber(number);
        return result;
    }
    
    /**
     * Функция задания графической панели.
     * @param panel панель.
     * @return удалось ли задать панель.
     */
    public boolean setPanel(GameFieldPanel panel) {
        if (panel != null) {
            _panel = panel;
            return true;
        }
        return false;
    }
    
    /**
     * Функция реагирующая на удаление рядов в стакане.
     * @param e количество рядов в удаленных из стакана.
     */
    @Override
    public void rowsArranged(GlassEvent e) {
        changeScore((int)e.getSource());
        identifyGameOver();
    }
    
    /**
     * Функция начисляет очки игроку за удаленные ряды.
     * @param removedLinesCount количество удаленных рядов.
     * @return удалосб ли начислить очки.
     */
    public boolean changeScore(int removedLinesCount) {
        int score = _model.score();
        switch(removedLinesCount) {
            case 1:
                score += 100;
                break;
            case 2:
                score += 400;
                break;
            case 3:
                score += 700;
                break;
            case 4:
                score += 1500;
                break;
            default:
                return false;                
        }
        _model.setScore(score);
        return true;
    }

    /**
     * Функция определяющая конец игры.
     * @return конец или не конец.
     */
    protected boolean identifyGameOver() {
        boolean result = false;
        for(int i = 0; i < _glass.notEndedLines().form().get(_glass.height()).length; i++) {
            if (_glass.notEndedLines().form().get(_glass.height())[i]) {
                result = true;
            }
        }
        if(result) {
            _timer.cancel();
             JOptionPane.showMessageDialog(null, "Game over!");
        }
        return result;
    }

/**
     * Статическая функция генерации фигуры по номеру.
     * @param number номер.
     * @return фигура.
     */
    public static Figure getFigureByNumber(int number) {
        Figure result;
        switch (number) {
            case (0):
                result = new TFigure();
                break;
            case (1):
                result = new LFigure();  
                break;
            case (2):
                result = new OFigure();
                break;
            case (3):
                result = new JFigure();  
                break;
            case (4):
                result = new IFigure();
                break;
            case (5):
                result = new SFigure();  
                break;
            case (6):
                result = new ZFigure();  
                break;
            default:
                result = new CompositeFigure();            
        }
        return result;
    }
}